#ifndef GRITC_GAME_CLIENT_HPP
#define GRITC_GAME_CLIENT_HPP

#include <map>
#include <string>
#include <algorithm>

namespace gritc { namespace game {

	struct client
	{
		struct factory
		{
			virtual ~factory() {}
			virtual void create(client&) = 0;
			virtual void update(client&) = 0;
			virtual void destroy(client&) = 0;
		};
		template<class Factory> struct derived_factory : factory
		{
			Factory factory_;
			derived_factory(Factory & f) : factory_(f) {}
			virtual ~derived_factory() {}
			virtual void create(client & c) { factory_.create(c); }
			virtual void update(client & c) { factory_.update(c); }
			virtual void destroy(client & c) { factory_.destroy(c); }
		};
		struct command
		{
			typedef std::string::iterator iterator;
			virtual ~command() {}
			virtual iterator call(client & c, iterator, iterator) = 0;
		};
		template<class T> struct derived_command : command
		{
			T f;
			derived_command(T & f_) : f(F_) {}
			virtual ~derived_command() {}
			virtual iterator call(client & c, iterator, iterator) { f(c); }
		};
		
		typedef std::map<std::string, command*> command_map;
		command_map commands_;
		command_map & commands() { return commands_; }

		typedef std::map<std::string, factory*> factory_map;
		
		factory_map scene_factories_;
		factory_map & scene_factories() { return scene_factories_; }

		virtual ~client()
		{
			for(factory_map::iterator i = scene_factories_.begin(); i != scene_factories_.end(); ++i)
				delete (*i).second;
			for(command_map::iterator i = commands_.begin(); i != commands_.end(); ++i)
				delete (*i).second;
		}
		template<class Command> void add_command(std::string & n, Command & c)
		{
			commands_.insert(std::make_pair(n, new derived_command<Command>(c)));
		}
		template<class String> void do_text(String & t)
		{
			const std::string WS(" \n\t\v\r");

			Iterator begin = t.begin();
			Iterator end = t.end();

			while(begin != end)
			{
				Iterator t = std::find_first_of(begin, end, WS.begin(), WS.end());
				std::string token(begin, t);

				command_map::iterator c = commands_.find(token);

				if(c == commands_.end())
					return;
				
				begin = (*c)->call(*this, t, end);
			}
		}
	};

	// server -> client
	// scene scene_id
	// add type id geometry position hp maxhp energy maxenergy
	// update id position hp maxhp energy maxenergy
	// remove id
	// action action_id avatar_id arguments
	// effect effect_id source_id target_id arguments

	// client -> server
	// login character position
	// update position
	// action action_id arguments
}}

#endif